D:\a\scloud-dns\scloud-dns\src\dns\packet\additional\mod.rs
Line | Count | Source |
1 | | use crate::dns::q_class::DNSClass; |
2 | | use crate::dns::q_name::parse_qname; |
3 | | use crate::dns::q_type::DNSRecordType; |
4 | | use crate::exceptions::SCloudException; |
5 | | |
6 | | #[derive(PartialEq, Debug)] |
7 | | pub(crate) struct AdditionalSection { |
8 | | pub(crate) q_name: String, |
9 | | pub(crate) q_type: DNSRecordType, |
10 | | pub(crate) q_class: DNSClass, |
11 | | pub(crate) ttl: u32, |
12 | | pub rdlength: u16, |
13 | | pub rdata: Vec<u8>, |
14 | | } |
15 | | |
16 | | impl AdditionalSection { |
17 | | /// Deserialize one AdditionalSection and return (section, consumed_bytes) |
18 | | /// |
19 | | /// # Exemple : |
20 | | /// ``` |
21 | | /// use crate::dns::packet::additional::AdditionalSection; |
22 | | /// use crate::dns::q_type::DNSRecordType; |
23 | | /// use crate::dns::q_class::DNSClass; |
24 | | /// |
25 | | /// // Record A in additional section (ns1.example.com → 192.0.2.1) |
26 | | /// let raw_additional: Vec<u8> = vec![ |
27 | | /// 0x03, b'n', b's', b'1', |
28 | | /// 0x07, b'e', b'x', b'a', b'm', b'p', b'l', b'e', |
29 | | /// 0x03, b'c', b'o', b'm', |
30 | | /// 0x00, // End of QNAME |
31 | | /// 0x00, 0x01, // TYPE = A |
32 | | /// 0x00, 0x01, // CLASS = IN |
33 | | /// 0x00, 0x00, 0x01, 0x2c, // TTL = 300 |
34 | | /// 0x00, 0x04, // RDLENGTH = 4 |
35 | | /// 192, 0, 2, 1, // RDATA |
36 | | /// ]; |
37 | | /// |
38 | | /// let (additional, consumed) = |
39 | | /// AdditionalSection::from_bytes(&raw_additional, 0).unwrap(); |
40 | | /// |
41 | | /// assert_eq!(additional.q_name, "ns1.example.com"); |
42 | | /// assert_eq!(additional.q_type, DNSRecordType::A); |
43 | | /// assert_eq!(additional.q_class, DNSClass::IN); |
44 | | /// assert_eq!(additional.ttl, 300); |
45 | | /// assert_eq!(additional.rdata, vec![192, 0, 2, 1]); |
46 | | /// assert_eq!(consumed, raw_additional.len()); |
47 | | /// ``` |
48 | 1 | pub(crate) fn from_bytes( |
49 | 1 | buf: &[u8], |
50 | 1 | offset: usize, |
51 | 1 | ) -> Result<(AdditionalSection, usize), SCloudException> { |
52 | 1 | let (q_name, consumed_name) = parse_qname(buf, offset).unwrap(); |
53 | 1 | let mut pos = consumed_name; |
54 | | |
55 | 1 | if buf.len() < pos + 10 { |
56 | 0 | return Err(SCloudException::SCLOUD_ADDITIONAL_DESERIALIZATION_FAILED_BUF_TOO_SHORT); |
57 | 1 | } |
58 | | |
59 | 1 | let q_type = DNSRecordType::try_from(u16::from_be_bytes([buf[pos], buf[pos + 1]])).unwrap(); |
60 | 1 | pos += 2; |
61 | | |
62 | 1 | let q_class = DNSClass::try_from(u16::from_be_bytes([buf[pos], buf[pos + 1]])).unwrap(); |
63 | 1 | pos += 2; |
64 | | |
65 | 1 | let ttl = u32::from_be_bytes([buf[pos], buf[pos + 1], buf[pos + 2], buf[pos + 3]]); |
66 | 1 | pos += 4; |
67 | | |
68 | 1 | let rdlength = u16::from_be_bytes([buf[pos], buf[pos + 1]]); |
69 | 1 | pos += 2; |
70 | | |
71 | 1 | if buf.len() < pos + rdlength as usize { |
72 | 0 | return Err( |
73 | 0 | SCloudException::SCLOUD_ADDITIONAL_DESERIALIZATION_FAILED_RDATA_OUT_OF_BOUNDS, |
74 | 0 | ); |
75 | 1 | } |
76 | | |
77 | 1 | let rdata = buf[pos..pos + rdlength as usize].to_vec(); |
78 | 1 | pos += rdlength as usize; |
79 | | |
80 | 1 | Ok(( |
81 | 1 | AdditionalSection { |
82 | 1 | q_name, |
83 | 1 | q_type, |
84 | 1 | q_class, |
85 | 1 | ttl, |
86 | 1 | rdlength, |
87 | 1 | rdata, |
88 | 1 | }, |
89 | 1 | pos, |
90 | 1 | )) |
91 | 1 | } |
92 | | |
93 | | /// Serialize the AdditionalSection into bytes |
94 | | /// |
95 | | /// # Exemple : |
96 | | /// ``` |
97 | | /// use crate::dns::packet::additional::AdditionalSection; |
98 | | /// use crate::dns::q_type::DNSRecordType; |
99 | | /// use crate::dns::q_class::DNSClass; |
100 | | /// |
101 | | /// let additional = AdditionalSection { |
102 | | /// q_name: "ns1.example.com".to_string(), |
103 | | /// q_type: DNSRecordType::A, |
104 | | /// q_class: DNSClass::IN, |
105 | | /// ttl: 300, |
106 | | /// rdlength: 4, |
107 | | /// rdata: vec![192, 0, 2, 1], |
108 | | /// }; |
109 | | /// |
110 | | /// let bytes = additional.to_bytes().unwrap(); |
111 | | /// |
112 | | /// // NAME + TYPE + CLASS + TTL + RDLENGTH + RDATA |
113 | | /// assert!(bytes.len() > 20); |
114 | | /// ``` |
115 | 1 | pub(crate) fn to_bytes(&self) -> Result<Vec<u8>, SCloudException> { |
116 | 1 | let mut buf: Vec<u8> = Vec::new(); |
117 | | |
118 | 3 | for label in self.q_name.split('.')1 { |
119 | 3 | let len = label.len(); |
120 | 3 | if len > 63 { |
121 | 0 | return Err( |
122 | 0 | SCloudException::SCLOUD_ADDITIONAL_DESERIALIZATION_FAILED_QNAME_TOO_LONG, |
123 | 0 | ); |
124 | 3 | } |
125 | 3 | buf.push(len as u8); |
126 | 3 | buf.extend_from_slice(label.as_bytes()); |
127 | | } |
128 | 1 | buf.push(0x00); |
129 | | |
130 | 1 | let qtype_u16 = |
131 | 1 | u16::try_from(self.q_type).expect("Cannot convert AdditionalSection q_type to u16"); |
132 | 1 | buf.extend_from_slice(&qtype_u16.to_be_bytes()); |
133 | | |
134 | 1 | let qclass_u16 = u16::try_from(self.q_class).unwrap(); |
135 | 1 | buf.extend_from_slice(&qclass_u16.to_be_bytes()); |
136 | | |
137 | 1 | buf.extend_from_slice(&self.ttl.to_be_bytes()); |
138 | 1 | buf.extend_from_slice(&self.rdlength.to_be_bytes()); |
139 | 1 | buf.extend_from_slice(&self.rdata); |
140 | | |
141 | 1 | Ok(buf) |
142 | 1 | } |
143 | | } |